package com.sys.object.common.utils;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class DataTypeCast
{
  protected Log logger = LogFactory.getLog(getClass());

  protected boolean emptyString2null = true;

  public void setEmptyString2null(boolean emptyString2null)
  {
    this.emptyString2null = emptyString2null;
  }

  public Object cast(Object value, Class<?> destClass)
  {
    if ((this.emptyString2null) && 
      ("".equals(value))) value = null;

    if (value == null) return null;

    if (destClass.isInstance(value)) {
      return value;
    }

    Class clazz = value.getClass();
    Object destValue = null;
    try {
      if (clazz == String.class) {
        destValue = fromString((String)value, destClass);
      }
      else if ((value instanceof Number)) {
        destValue = fromNumber((Number)value, destClass);
      }
      else if ((clazz == Boolean.class) || (clazz == Boolean.TYPE)) {
        destValue = fromBoolean((Boolean)value, destClass);
      }
      else if ((value instanceof java.util.Date)) {
        destValue = fromDate((java.util.Date)value, destClass);
      }
      else if (((value instanceof List)) || ((value instanceof Set))) {
        destValue = fromCollection((Collection)value, destClass);
      }
      else {
        this.logger.error("not supported type:" + destClass.getName());
        throw new RuntimeException("not supported type:" + destClass.getName());
      }
    }
    catch (Exception e) {
      String info = value.getClass().getName() + " to " + destClass.getName() + " :" + value.toString();
      this.logger.error(info, e);
      throw new RuntimeException(info, e);
    }
    return destValue;
  }

  protected <E> Object fromString(String value, Class<E> clazz)
    throws Exception
  {
    Object destValue = null;

    if ("".equals(value)) return null;

    if ((clazz == Boolean.class) || (clazz == Boolean.TYPE)) {
      if (("1".equals(value)) || ("true".equalsIgnoreCase(value)) || ("yes".equalsIgnoreCase(value))) {
        destValue = Boolean.valueOf(true);
      }
      else {
        destValue = Boolean.valueOf(false);
      }
    }
    else if (Number.class.isAssignableFrom(clazz))
    {
      value = value.replaceAll(",", "");

      if ((clazz == Integer.class) || (clazz == Integer.TYPE)) {
        destValue = Integer.valueOf(value);
      }
      else if ((clazz == Long.class) || (clazz == Long.TYPE)) {
        destValue = Long.valueOf(value);
      }
      else if ((clazz == Float.class) || (clazz == Float.TYPE)) {
        destValue = Float.valueOf(value);
      }
      else if ((clazz == Double.class) || (clazz == Double.TYPE)) {
        destValue = Double.valueOf(value);
      }
      else if ((clazz == Byte.class) || (clazz == Byte.TYPE)) {
        destValue = new Byte(value);
      }
      else if ((clazz == Short.class) || (clazz == Short.TYPE)) {
        destValue = new Short(value);
      }
      else if (clazz == BigDecimal.class) {
        destValue = new BigDecimal(value);
      }
      else if (clazz == BigInteger.class) {
        destValue = new BigInteger(value);
      }
      else
      {
        Constructor con = clazz.getConstructor(new Class[] { String.class });
        destValue = con.newInstance(new Object[] { value });
      }
    }
    else if (clazz.isPrimitive()) {
      String name = clazz.getName();

      value = value.replaceAll(",", "");
      BigDecimal dec = new BigDecimal(value);

      if (name == "int") {
        destValue = Integer.valueOf(dec.intValue());
      }
      else if (name == "long") {
        destValue = Long.valueOf(dec.longValue());
      }
      else if (name == "float") {
        destValue = Float.valueOf(dec.floatValue());
      }
      else if (name == "double") {
        destValue = Double.valueOf(dec.doubleValue());
      }
      else if (name == "byte") {
        destValue = Byte.valueOf(dec.byteValue());
      }
      else if (name == "short") {
        destValue = Short.valueOf(dec.shortValue());
      }
    }
    else if (java.util.Date.class.isAssignableFrom(clazz)) {
      if (java.sql.Date.class.isAssignableFrom(clazz)) {
        destValue = UtilDate.toSqlDate(value);
      }
      else if (Timestamp.class.isAssignableFrom(clazz)) {
        destValue = UtilDate.toSqlTimestamp(value);
      }
      else if (java.util.Date.class.isAssignableFrom(clazz)) {
        destValue = UtilDate.toDate(value);
      }

      String timeZoneId = null;
//      IUser user = LoginUserHolder.getLoginUser();
//      if (user != null) timeZoneId = user.getTimeZoneId();

      if (timeZoneId != null) {
        TimeZone tz = TimeZone.getTimeZone(timeZoneId);
        destValue = UtilDate.convert((Timestamp)destValue, TimeZone.getDefault(), tz);
      }
    }
    else if ((clazz == Character.class) || (clazz == Character.TYPE)) {
      destValue = Character.valueOf(value.charAt(0));
    }

    return destValue;
  }

  protected <E> Object fromNumber(Number value, Class<E> clazz)
    throws Exception
  {
    Object destValue = null;

    if (String.class.isAssignableFrom(clazz)) {
      destValue = value.toString();
    }
    else if (clazz.getName() == "boolean")
    {
      destValue = Boolean.valueOf(value.intValue() > 0);
    }
    else if (Boolean.class.isAssignableFrom(clazz)) {
      destValue = Boolean.valueOf(value.intValue() > 0);
    }
    else if (Number.class.isAssignableFrom(clazz)) {
      if ((clazz == Integer.class) || (clazz == Integer.TYPE)) {
        destValue = Integer.valueOf(value.intValue());
      }
      else if ((clazz == Long.class) || (clazz == Long.TYPE)) {
        destValue = Long.valueOf(value.longValue());
      }
      else if ((clazz == Float.class) || (clazz == Float.TYPE)) {
        destValue = Float.valueOf(value.floatValue());
      }
      else if ((clazz == Double.class) || (clazz == Double.TYPE)) {
        destValue = Double.valueOf(value.doubleValue());
      }
      else if ((clazz == Byte.class) || (clazz == Byte.TYPE)) {
        destValue = Byte.valueOf(value.byteValue());
      }
      else if ((clazz == Short.class) || (clazz == Short.TYPE)) {
        destValue = Short.valueOf(value.shortValue());
      }
      else if (clazz == BigInteger.class) {
        destValue = new BigDecimal(value.toString()).toBigInteger();
      }
      else if (clazz == BigDecimal.class) {
        destValue = new BigDecimal(value.toString());
      }

    }
    else if (clazz.isPrimitive()) {
      if (clazz.getName() == "int") {
        destValue = Integer.valueOf(value.intValue());
      }
      else if (clazz.getName() == "long") {
        destValue = Long.valueOf(value.longValue());
      }
      else if (clazz.getName() == "float") {
        destValue = Float.valueOf(value.floatValue());
      }
      else if (clazz.getName() == "double") {
        destValue = Double.valueOf(value.doubleValue());
      }
      else if (clazz.getName() == "byte") {
        destValue = Byte.valueOf(value.byteValue());
      }
      else if (clazz.getName() == "short") {
        destValue = Short.valueOf(value.shortValue());
      }
    }
    else if (java.util.Date.class.isAssignableFrom(clazz))
    {
      long val = value.longValue();

      destValue = new Timestamp(val);
    }
    else if ((clazz == Character.class) || (clazz == Character.TYPE)) {
      char ch = (char)(int)value.longValue();
      destValue = Character.valueOf(ch);
    }

    return destValue;
  }

  protected <E> Object fromBoolean(Boolean value, Class<E> clazz)
    throws Exception
  {
    Object destValue = null;

    if (String.class.isAssignableFrom(clazz)) {
      destValue = value.booleanValue() ? "1" : "0";
    }
    else if (Number.class.isAssignableFrom(clazz)) {
      int v = value.booleanValue() ? 1 : 0;
      if ((clazz == Integer.class) || (clazz == Integer.TYPE)) {
        destValue = Integer.valueOf(v);
      }
      else if ((clazz == Long.class) || (clazz == Long.TYPE)) {
        destValue = Long.valueOf(v);
      }
      else if ((clazz == Float.class) || (clazz == Float.TYPE)) {
        destValue = Float.valueOf(v);
      }
      else if ((clazz == Double.class) || (clazz == Double.TYPE)) {
        destValue = Double.valueOf(v);
      }
      else if ((clazz == Byte.class) || (clazz == Byte.TYPE)) {
        destValue = Byte.valueOf((byte)v);
      }
      else if ((clazz == Short.class) || (clazz == Short.TYPE)) {
        destValue = Short.valueOf((byte)v);
      }
      else if (clazz == BigInteger.class) {
        String sv = value.booleanValue() ? "1" : "0";
        destValue = new BigInteger(sv);
      }
      else if (clazz == BigDecimal.class) {
        destValue = new BigDecimal(v);
      }

    }
    else if (clazz.isPrimitive()) {
      if (clazz.getName() == "int") {
        destValue = Integer.valueOf(value.booleanValue() ? 1 : 0);
      }
      else if (clazz.getName() == "long") {
        destValue = Long.valueOf(value.booleanValue() ? 1 : 0);
      }
      else if (clazz.getName() == "float") {
        destValue = Float.valueOf(value.booleanValue() ? 1 : 0);
      }
      else if (clazz.getName() == "double") {
        destValue = Double.valueOf(value.booleanValue() ? 1 : 0);
      }
      else if (clazz.getName() == "byte") {
        destValue = Byte.valueOf(value.booleanValue() ? "1" : "0");
      }
      else if (clazz.getName() == "short") {
        destValue = Short.valueOf(value.booleanValue() ? "1" : "0");
      }
      else if (clazz.getName() == "boolean") {
        destValue = Short.valueOf(value.booleanValue() ? "1" : "0");
      }

    }
    else if ((clazz == Character.class) || (clazz == Character.TYPE)) {
      char ch = value.booleanValue() ? '1' : '0';
      destValue = Character.valueOf(ch);
    }
    else if (clazz.getName() == "boolean")
    {
      destValue = value;
    }

    return destValue;
  }

  protected <E> Object fromDate(java.util.Date value, Class<E> clazz)
    throws Exception
  {
    Object destValue = null;

    if (String.class.isAssignableFrom(clazz)) {
      destValue = value.toString();
    }
    else if ((value instanceof java.sql.Date))
    {
      if (Timestamp.class.isAssignableFrom(clazz)) {
        destValue = new Timestamp(value.getTime());
      }
    }
    else if ((value instanceof Timestamp)) {
      Timestamp stamp = (Timestamp)value;
      if (java.sql.Date.class.isAssignableFrom(clazz)) {
        destValue = new java.sql.Date(stamp.getTime());
      }
    }

    return destValue;
  }

  protected <E> Object fromCollection(Collection<E> value, Class<E> clazz)
    throws Exception
  {
    Collection set = (Collection)clazz.newInstance();
    set.addAll(value);

    return set;
  }
}
